package com.sumainfor.common.utlis;

import com.google.common.collect.Sets;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author EddyChen
 * @Email 835033913@qq.com
 * @Create 2019/7/16 12:19
 * 全文检索工具
 */
public class LuceneUtils {

    private static Directory directory;
    private static Analyzer analyzer;

    private LuceneUtils() {
    }

    static {
        try {
            //内存目录，会把索引库保存在内存。
            directory = new RAMDirectory();
            //文件系统目录，会把索引库指向本地磁盘。
//            directory = FSDirectory.open(Paths.get("D:\\indexDir"));
//            analyzer = new StandardAnalyzer();
            //引入IK分词器
            analyzer = new IKAnalyzer();
        } catch (Exception e) {
            e.printStackTrace();

        }
    }


    /**
     * 功能描述:
     *
     * @param originalList 数据列表
     * @param queryColumn  查询的关键字段，索引
     * @param keyWord      查询的关键字
     * @param id           唯一表示ID
     * @Author zhlu
     * @Description //TODO
     * @Date 2019/7/17 13:44
     */
    public static List<Map<String, Object>> query(List<Map<String, Object>> originalList, List<String> queryColumn, String id, String[] keyWord) throws IOException, ParseException {

        if (StringUtils.isBlank(id)) {
            throw new RuntimeException("请提供主键ID字段名");
        }

        if (CollectionUtils.isEmpty(originalList)) {
            return originalList;
        }

        if (CollectionUtils.isEmpty(queryColumn)) {
            throw new RuntimeException("请提供查询的列名");
        }

        if (!queryColumn.contains(id)) {
            queryColumn.add(id);
        }
        IndexWriter indexWriter = null;
        try {

            //写索引的配置类，配置使用的分词器为标准分词器
            IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
            //IndexWriter是lucene的核心类，用于存储索引
            indexWriter = new IndexWriter(directory, indexWriterConfig);

            //创建分词索引文档
            for (Map<String, Object> map : originalList) {
                addIndexDocs(indexWriter, map, queryColumn);
            }
            indexWriter.commit();
            DirectoryReader open = DirectoryReader.open(directory);
            IndexSearcher searcher = new IndexSearcher(open);


            //构建查询 条件
            BooleanQuery.Builder builder = new BooleanQuery.Builder();
            for (String key : queryColumn) {
                QueryParser parser = new QueryParser(key, analyzer);
                for (String s : keyWord) {
                    Query query = parser.parse(s);
                    builder.add(query, BooleanClause.Occur.SHOULD);
                }
            }


            // 创建查询对象
            TopDocs topDocs = searcher.search(builder.build(), originalList.size() * 10);

            Set<String> idVal = Sets.newHashSet();

            for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
                Document doc = searcher.doc(scoreDoc.doc);
                String s = doc.get(id);
                if (StringUtils.isNotBlank(s)) {
                    idVal.add(s);
                }
            }

            return originalList.stream().filter(dat -> idVal.contains(MapUtils.getString(dat, id))).collect(Collectors.toList());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (indexWriter != null) {
                // 删除所有
                indexWriter.deleteAll();
                // 提交
                indexWriter.commit();
                // 关闭
                indexWriter.close();
            }
        }
        return null;
    }


    private static void addIndexDocs(IndexWriter indexWriter, Map<String, Object> params, List<String> queryColumn) throws IOException {
        //存入的文档
        Document document = new Document();
        for (Map.Entry<String, Object> entry : params.entrySet()) {

            //存入文档的属性,第一个是字段名，第二个是内容，第三个是否存储内容
            //Field有很多实现类，对于不同类型的字段，有不同的实现类来操作StringField是存储String类型的字段，不进行分词
            Field field = null;
            if (CollectionUtils.isNotEmpty(queryColumn)) {
                if (queryColumn.contains(entry.getKey())) {
                    field = new TextField(entry.getKey(), entry.getValue().toString(), Field.Store.YES);
                }
            } else {
                field = new StoredField(entry.getKey(), entry.getValue().toString());
            }
            if (field == null) {
                return;
            }
            //将属性加入文档中
            document.add(field);
            //把文档交给indexWriter
            indexWriter.addDocument(document);
        }
    }


    public static void main(String[] args) throws IOException, ParseException {
        List<Map<String, Object>> originalList = new ArrayList<Map<String, Object>>();
        Map<String, Object> map = new HashMap<>();
        map.put("id", "1");
        map.put("name", "控江");
        map.put("age", 12);
        map.put("like", "听歌，打球，跑步，写程序");

        Map<String, Object> map1 = new HashMap<>();
        map1.put("name", "张三");
        map1.put("id", "2");
        map1.put("age", 12);
        map1.put("like", "上网，玩游戏，看书，写程序");

        Map<String, Object> map2 = new HashMap<>();
        map2.put("name", "王麻子");
        map2.put("age", 12);
        map2.put("id", "3");
        map2.put("like", "打篮球。踢足球。打乒乓球，写程序");
        originalList.add(map);
        originalList.add(map1);
        originalList.add(map2);

        List<String> queryColumn = new ArrayList<String>();
        queryColumn.add("name");
        queryColumn.add("like");

        query(originalList, queryColumn, "id", new String[]{"乒乓", "控江"}).forEach(System.out::println);
    }

}
